#pragma once

#include <rocksdb/env.h>
#include <rocksdb/slice.h>

#include <memory>
#include <string>

class InternalValue {
 public:
  InternalValue(const rocksdb::Slice &user_value)
      : start_(nullptr), user_value_(user_value), version_(0), timestamp_(0) {}

  virtual ~InternalValue() {
    if (start_ != space_) {
      delete[] start_;
    }
  }

  void SetRelativeTimeStamp(int32_t ttl) {
    int64_t unix_time;
    rocksdb::Env::Default()->GetCurrentTime(&unix_time);
    timestamp_ = static_cast<int32_t>(unix_time) + ttl;
  }

  void set_timestamp(int32_t timestamp = 0) { timestamp_ = timestamp; }
  void set_version(int32_t version = 0) { version_ = version; }

  virtual const rocksdb::Slice Encode() {
    size_t usize = user_value_.size();
    size_t needed = usize + kDefaultValueSuffixLength;
    char *dst_ptr;
    if (needed <= sizeof(space_)) {
      dst_ptr = space_;
    } else {
      dst_ptr = new char[needed];
      if (start_ != space_) {
        delete[] start_;
      }
    }

    start_ = dst_ptr;
    size_t len = AppendTimestampAndVersion();
    return rocksdb::Slice(start_, len);
  }

  virtual size_t AppendTimestampAndVersion() = 0;

  static const size_t kDefaultValueSuffixLength = sizeof(int32_t) * 2;

 protected:
  char space_[200];
  char *start_;
  rocksdb::Slice user_value_;
  int32_t version_;
  int32_t timestamp_;
};

class ParsedInternalValue {
 public:
  ParsedInternalValue(std::string *value)
      : value_(value), version_(0), timestamp_(0) {}

  ParsedInternalValue(const rocksdb::Slice &value)
      : value_(nullptr), version_(0), timestamp_(0) {}

  virtual ~ParsedInternalValue() = default;

  rocksdb::Slice user_value() { return user_value_; }

  int32_t verison() { return version_; }

  void set_version(int32_t version) {
    version_ = version;
    SetVersionToValue();
  }

  void set_timestamp(int32_t timestamp) {
    timestamp_ = timestamp;
    SetTimeStampToValue();
  }

  int32_t timestamp() { return timestamp_; }

  void SetRelativeTimeStamp(int32_t ttl) {
    int64_t unix_time;
    rocksdb::Env::Default()->GetCurrentTime(&unix_time);
    timestamp_ = static_cast<int32_t>(unix_time) + ttl;
    SetTimeStampToValue();
  }

  bool IsExpired() {
    if (timestamp_ == 0) {
      return false;
    }

    int64_t unix_time;
    rocksdb::Env::Default()->GetCurrentTime(&unix_time);
    return timestamp_ < unix_time;
  }

  bool IsStale() {
    if (timestamp_ == 0) {
      return false;
    }

    int64_t unix_time;
    rocksdb::Env::Default()->GetCurrentTime(&unix_time);
    return timestamp_ < unix_time;
  }

  bool IsPermanentSurvival() { return timestamp_ == 0; }

  virtual void StripSuffix() = 0;

 protected:
  virtual void SetVersionToValue() = 0;
  virtual void SetTimeStampToValue() = 0;

  std::string *value_;
  rocksdb::Slice user_value_;
  int32_t version_;
  int32_t timestamp_;
};
